<!-- 扫码支付 -->
<template>
  <view class="pay-box">
    <slot name="title">
      <view class="title">请出示付款码</view>
      <view class="sub-title">将您的付款码对准摄像头</view>
    </slot>

    <view class="content-pay">
      <view
        class="flex justify-center items-center relative bg-slate-100 rounded-md p-10rpx"
        style="display: flex; margin: 10rpx 0px"
      >
        <u-icon name="scan" size="80rpx"></u-icon>
      </view>

      <view class="text-slate-100 text-sm absolute bottom-0">{{ payBean.payCode }}</view>

      <text class="info-tip" v-if="payBean.infoTxt">{{ payBean.infoTxt }}</text>
      <text class="err-tip" v-if="payBean.errTxt">{{ payBean.errTxt }}</text>

      <template v-if="props.showPaytype">
        <u-line margin="0.5em 0" color="lightgrey" />
        <view class="flex w-50rpx justify-between">
          <u-icon name="zhifubao" size="20rpx" bold color="#3c9cff"></u-icon>
          <u-icon name="weixin-fill" size="20rpx" color="#08e0ad"></u-icon>
        </view>
      </template>
    </view>
    <!-- 正在支付遮罩层 -->
    <payMask ref="refPayMask" />

    <!-- 弹窗提示 - 主要用于显示一些错误信息 -->
    <resultPopup ref="refCommonPopup"> </resultPopup>
  </view>
</template>
<script setup>
import { onBeforeUnmount, onMounted, reactive, ref } from "vue";
import { asyncExecFunction } from "@/king-components/js/KingonPlus";
import { identifyPaymentCode, generateSerialNumber } from "@/utils/util";
import { initScanBox, destroyScanBox } from "./scanUtils";
import resultPopup from "@/components/common-popup/result-popup.vue";
import dayjs from "dayjs";
import payMask from "./pay-mask.vue";
import { useStore } from "vuex";
import { payBuffetOrder, pcSave } from "@/api";
import { getCurrentMealType } from "./mealTypeRulesUtils";
import { getDeviceConfig } from "@/common/devices-config";

const store = useStore();

const refCommonPopup = ref();
const props = defineProps({
  showPaytype: {
    type: Boolean,
    default: true,
  },
  qrSize: {
    type: Number,
    default: 150,
  },
  /** 跳过支付., 只做操作组件 */
  skipPayment: {
    type: Boolean,
    default: false,
  },
  // hookPlaceAnOrder: {
  //   type: Function,
  //   // 不使用时请不要传递该参数 ，必须是 async 函数
  // },
});

// (!)接口返回的支付状态 支付订单状态 NO_PAY-待支付, FAIL-失败, SUCCESS-成功, CLOSE-支付关闭

const payBean = reactive({
  // 支付方式(此处指该组件的固定类型名称，用于区分收款结束时的展示状态)
  payType: "scan",
  // 支付(此处指刷卡/人脸)方式
  way: 0,
  // 支付状态  'reading' 'paying', 'success', 'fail', 'cancel', 'timeout'
  status: "reading",
  // 支付金额
  amount: 0,
  // 显示消息
  infoTxt: "将支付二维码对准摄像头", //
  // 错误消息
  errTxt: "", // 支付失败 / 二维码过期
  // 支付结果
  result: {},
  // 下单成功后的订单号（ pcSave 返回的是订单组id, 能查到子订单的简略信息）
  orderId: "",
  //--- 特殊参数 -----------------------------------------------------------------------------------
  // 支付二维码
  qrcode: "xxxxx-xxx-xxx-xxx",
  // 扫码结果（付款码）
  payCode: "",
  // 付款码类型
  codeType: "",
  // 支付配置（平台的支付配置）
  payMode: "",
});

onMounted(async () => {
  initScanCode();
  // 获取支付配置, 来显示是否支持 微信支付宝支付
  // try {
  //   const pam = {
  //     payCode: import.meta.env.VITE_PAY_CODE,
  //   }
  //   const { data } = await getPayMode(pam);
  // } catch (error) {
  //   console.error("平台支付配置获取失败",error);
  // }
});

onBeforeUnmount(() => {
  asyncExecFunction("HnXfjPortClose"); // 关闭串口
});

const statusTips = {
  SCAN: {
    reading: () => {
      payBean.infoTxt = "请出示付款码";
      payBean.errTxt = "";
      payBean.status = "reading";
    },
    placing: () => {
      payBean.infoTxt = "正在下单";
      payBean.errTxt = "";
      payBean.status = "placing";
    },
    paying: () => {
      payBean.infoTxt = "正在支付";
      payBean.errTxt = "";
      payBean.status = "paying";
    },
    success: () => {
      payBean.infoTxt = "支付成功";
      payBean.errTxt = "";
      payBean.status = "success";
    },
    fail: (errTxt, openPopup = false, reRead = false) => {
      payBean.infoTxt = "";
      payBean.errTxt = errTxt || "支付失败";
      payBean.status = "fail";
      if (openPopup) {
        // 弹出错误提示框
        refCommonPopup.value.open({
          title: "失败",
          content: errTxt,
          type: "fail",
          //----
          // 自定义数据
          onConfirm: () => {
            // 重置表单
            payBean.payCode = "";
            payBean.qrcode = "";
            payBean.status = "reading";
            if (reRead) initScanCode();
          },
        });
      }
    },
  },
};

// 扫码功能使用的模式  串口参数模式 serialPortParameterMode / 键盘监听模式 keyboardMonitoringMode
const scanMode = ref("serialPortParameterMode");

const refPayMask = ref();
const initScanCode = async () => {
  // 重置消息
  statusTips.SCAN.reading();
  const scanDeviceConfig = getDeviceConfig("scan");
  if (scanDeviceConfig.enabledParams === false) {
    scanMode.value = "keyboardMonitoringMode";
    // 监听键盘事件
    initScanBox(async (code) => {
      payBean.payCode = code;
      // 判断付款吗是否正确 （此处只考虑微信和支付宝）
      const codeType = identifyPaymentCode(code);
      if (!codeType) {
        payBean.payCode = "";
        return statusTips.SCAN.fail("付款码错误,请重新扫码");
      }
      // 正确后立即取消监听并执行支付操作, 支付失败则再次开启监听
      destroyScanBox();
      // 如果设置了跳过支付则直接结束，返回支付信息交由外部处理
      if (props.skipPayment === true) return payFinsh();
      // 执行下单逻辑
      payBean.codeType = codeType;
      // 如果当前订单已存在，则直接执行支付逻辑
      if (payBean.orderId) return await payment(payBean.orderId, codeType);
      else await placeAnOrder(codeType);
    }, false);
  } else {
    scanMode.value = "serialPortParameterMode";
    try {
      const result = await asyncExecFunction("HnXfjPortInit", getDeviceConfig("scan").params);
      // 初始化
      const data = JSON.parse(result);
      if (data.Action == "OnCardreaded") {
        console.log("读取到付款码 ->", data);
        const code = data.CardNumb;
        payBean.payCode = code;
        // 判断付款吗是否正确 （此处只考虑微信和支付宝）
        const codeType = identifyPaymentCode(code);
        if (!codeType) {
          payBean.payCode = "";
          return statusTips.SCAN.fail("付款码错误,请重新扫码");
        }
        // 正确后立即取消监听并执行支付操作, 支付失败则再次开启监听
        asyncExecFunction("HnXfjPortClose"); // 关闭串口
        // 如果设置了跳过支付则直接结束，返回支付信息交由外部处理
        if (props.skipPayment === true) return payFinsh();
        // 执行下单逻辑
        payBean.codeType = codeType;
        // 如果当前订单已存在，则直接执行支付逻辑
        if (payBean.orderId) return await payment(payBean.orderId, codeType);
        else await placeAnOrder(codeType);
      } else {
        throw new Error("初始化失败");
      }
    } catch (error) {
      console.error(error);
      uni.showModal({ title: "提示", content: error.message });
      const errText = error.data?.msg || (typeof error.message == "string" ? error.message : "未知错误");
      bean.errTxt = errText;
      // 抛出异常
      throw error;
    }
  }
};

/** 下单逻辑 */
const placeAnOrder = async (codeType) => {
  refPayMask.value.open();
  refPayMask.value.setDate("下单中", "请稍等...");
  try {
    // 商家信息
    const { businessId, businessName } = store.getters.businessInfo;
    // 其他待支付商品信息
    const productDetails = store.getters.currShoppingCardInfo;
    // 订单列表 （也可以从 provide 中获取）
    const orderItems = productDetails.productList.map((v) => {
      return {
        foodName: v.name,
        foodNum: v.count,
        price: v.price,
        orderPackageItemId: v.foodTypeId,
        buffetOrderItemId: v.foodTypeId,
      };
    });

    const pam = {
      amountcollected: productDetails.totalPrice,
      customerInformationName: productDetails?.useMember ? productDetails.member?.customerName : "",
      phone: productDetails?.member.customerPhone,
      businessName: businessName,
      diningStyle: productDetails?.isPack ? 3 : 2, // TODO 暂时写死	就餐方式(1送餐2堂食3自提)
      mealTime: dayjs().add(1, "hour").format("YYYY-MM-DD HH:mm:ss"), //TODO	用餐时间 当前消费机场景下无
      mealtype: "", // 餐别、餐次（1早餐2午餐3晚餐4下午茶5宵夜
      mealType: getCurrentMealType(), // 餐别
      orderRemark: "", // 订单备注
      payType: codeType === "alipay" ? 3 : 2, // 支付方式（1余额2微信3支付宝4现金5套餐6刷卡(注意不是会员卡)7货到付款）
      buffetOrderItemEntityList: orderItems,
      businessId: businessId,
      // foodName: "",
      knCustomerInformationId: productDetails.useMember ? productDetails?.member.customerInformationId : "",
      // foodAreaName: "", (?)
      orderType: 1, // orderType	订单类型（1自选订单2周期订单3日照点餐订单）
      dcsource: 8, // 	下单方式（1商家app2小程序4家庭版app5送餐人员app、6电话下单 7日照PC端8消费机）
      // orderStatus: 1, // 	订单状态（1未出单2已出单、未领 3已领餐、4已送达5逾期6退单7已评论8已派单9待派单10已取消）
      isUse: "2", // 是否使用餐补(1是 2否)
      payStatus: "0", // 	支付状态（0未支付 1已支付）
      packingFee: 0, // 包装费
      distributionFee: 0, // 	配送费,
      serialNumber: generateSerialNumber(), // 前端自建流水号
    };
    const { data, success, msg } = await pcSave(pam);
    if (!success && data) throw new Error(msg || "下单失败");
    payBean.orderId = data;
    // 下单成功后，执行支付逻辑

    // 支付逻辑  
    // { // 返回值
    //  "buffetOrderId": "1920779696527486978",
    //  "parentId": "1920779696305188865",
    //  "qrCodeUrl": null,
    //  "expireTime": null,
    //  "payOrderId": null
    // }
    await payment(data.parentId, codeType);
  } catch (error) {
    console.error(error);
    refPayMask.value.close();
    statusTips.SCAN.fail(error.message || "下单失败", true, true);
  }
};

/**
 * 支付逻辑
 * @param codeType alipay 支付宝 wechatPay 微信
 */
const payment = async (parentId, codeType) => {
  statusTips.SCAN.paying();
  refPayMask.value.open();
  /**
   * 扫码支付与其他支付的差异
   * 1. paymentBarcode 收款码
   * 3.  payType: "需要根据codeType判断"
   */
  refPayMask.value.setDate("支付中", "请等待支付结果");
  try {
    const pam = {
      // payType: "1", // 支付方式（1余额2微信3支付宝4现金5套餐6刷卡(注意不是会员卡)7货到付款）
      parentId: parentId,
      payType: codeType === "alipay" ? 3 : 2, // 支付方式（1余额2微信3支付宝4现金5套餐6刷卡(注意不是会员卡)7货到付款）
      paymentBarcode: payBean.payCode, // 收款码
    };
    const { data, success } = await payBuffetOrder(pam);
    // console.log("支付结果", { data, success });
    if (!success) throw new Error("支付失败");
    // @payStatus  NO_PAY-待支付, FAIL-失败, SUCCESS-成功, CLOSE-支付关闭
    if (data.payStatus != "SUCCESS") throw new Error(data.remark || "支付失败");
    refPayMask.value.close();
    statusTips.SCAN.success("支付成功");
    payFinsh(data);
  } catch (error) {
    // console.error("支付异常", { error: error.message, refPayMask: refPayMask.value, statusTips });
    if (error?.data?.msg == "订单已经支付请刷新") {
      refPayMask.value.close();
      statusTips.SCAN.success("订单已支付");
      return payFinsh({});
    }
    refPayMask.value.close();
    statusTips.SCAN.fail(error.message || "支付失败", true, true);
    // 重新抛出异常
    throw error;
  }
};

const hookPayment = async (pam) => {
  statusTips.SCAN.paying();
  refPayMask.value.open();
  /**
   * 扫码支付与其他支付的差异
   * 1. paymentBarcode 收款码
   * 3.  payType: "需要根据codeType判断"
   */
  refPayMask.value.setDate("支付中", "请等待支付结果");
  try {
    if (!pam || !pam.payType || !pam.paymentBarcode) {
      throw new Error("支付参数错误");
    }
    const { data, success } = await payBuffetOrder(pam);
    if (!success) throw new Error("支付失败");
    refPayMask.value.close();
    statusTips.SCAN.success("支付成功");
    payFinsh(data);
  } catch (error) {
    if (error?.data.msg == "订单已经支付请刷新") {
      refPayMask.value.close();
      statusTips.SCAN.success("订单已支付");
      return payFinsh({});
    }
    refPayMask.value.close();
    statusTips.SCAN.fail(error.message || "支付失败", true, true);
    // 重新抛出异常
    throw error;
  }
};

// 支付结束
const payFinsh = (result) => {
  payBean.result = result;
  statusTips.SCAN.success();
  refPayMask.value.close();
  uni.$emit("pay:finish", { type: payBean.status, money: payBean.amount, payBean: payBean, result: payBean.result });
  emits("pay-finish", { type: payBean.status, money: payBean.amount, payBean: payBean, result: payBean.result });
};

// pay-finish 支付结束事件 : ({payBean 页面支付状态, result 支付结果(一般是接口返回的结果)}) => ({})
const emits = defineEmits(["pay-finish"]);
</script>
<style lang="scss" scoped>
.pay-box {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 10rpx;
  width: 100%;
  box-sizing: border-box;

  .title {
    margin-top: 20rpx;
    font-weight: bold;
  }

  .sub-title {
    margin-top: 10rpx;
    font-size: 10rpx;
    color: lightgray;
  }

  .content-pay {
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;

    .err-tip {
      color: red;
      font-size: 8rpx;
      display: block;
      padding: 5rpx 0;
      max-width: 80%;
      text-align: center;
    }

    .info-tip {
      color: lightgray;
      font-size: 8rpx;
      display: block;
      padding: 5rpx 0;
    }
  }

  .hide-input {
    width: 0;
    height: 0;
  }
}
</style>
